package com.blue.alipay.controller;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.GoodsDetail;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeQueryRequestBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeRefundRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.model.result.AlipayF2FQueryResult;
import com.alipay.demo.trade.model.result.AlipayF2FRefundResult;
import com.alipay.demo.trade.service.AlipayMonitorService;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AlipayMonitorServiceImpl;
import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl;
import com.alipay.demo.trade.service.impl.AlipayTradeWithHBServiceImpl;
import com.alipay.demo.trade.utils.ZxingUtils;
import com.blue.alipay.contants.AlipayConfig;
import com.blue.alipay.utils.controller.BaseController;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.util.ArrayList;
import java.util.List;


/**
 * @描述 面对面支付 扫码支付
 * @时间 2017-9-22 17:20
 * @作者 肖亦鸣
 */
@Controller
@RequestMapping("facepay")
public class AliFacePayController extends BaseController {
    private static Logger logger = LogManager.getLogger(AliWapPayController.class.getName());

    private final String PATH = "facepay/";
    // 支付宝当面付2.0服务
    private static AlipayTradeService tradeService;
    // 支付宝当面付2.0服务（集成了交易保障接口逻辑）
    private static AlipayTradeService tradeWithHBService;
    // 支付宝交易保障接口服务，供测试接口api使用，请先阅读readme.txt
    private static AlipayMonitorService monitorService;

    static {
        /** 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
         *  Configs会读取classpath下的zfbinfo.properties文件配置信息，如果找不到该文件则确认该文件是否在classpath目录
         */
        Configs.init("zfbinfo.properties");

        /** 使用Configs提供的默认参数
         *  AlipayTradeService可以使用单例或者为静态成员对象，不需要反复new
         */
        tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

        // 支付宝当面付2.0服务（集成了交易保障接口逻辑）
        tradeWithHBService = new AlipayTradeWithHBServiceImpl.ClientBuilder().build();

        /** 如果需要在程序中覆盖Configs提供的默认参数, 可以使用ClientBuilder类的setXXX方法修改默认参数 否则使用代码中的默认设置 */
        monitorService = new AlipayMonitorServiceImpl.ClientBuilder()
                .setGatewayUrl("http://mcloudmonitor.com/gateway.do").setCharset("GBK")
                .setFormat("json").build();
    }

    @RequestMapping("index.html")
    public String index() throws Exception {
        logger.info("index...");
        return PATH + "index";
    }

    @RequestMapping("precreate.html")
    public String precreate() throws Exception {
        logger.info("precreate...");
        return PATH + "precreate";
    }


    @RequestMapping("doprecreate.html")
    public String doPreCreate() throws Exception {
        logger.info("doPreCreate...");
        logger.info("requestParams:{}", JSON.toJSONString(request.getParameterMap()));
        // (必填) 商户网站订单系统中唯一订单号，64个字符以内，只能包含字母、数字、下划线，
        // 需保证商户系统端不能重复，建议通过数据库sequence生成，
        String outTradeNo = "tradeprecreate" + System.currentTimeMillis() + (long) (Math.random() * 10000000L);
        // (必填) 订单标题，粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = "测试扫码支付0.01元";
        // (必填) 订单总金额，单位为元，不能超过1亿元
        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
        String totalAmount = "0.01";
        // (可选) 订单不可打折金额，可以配合商家平台配置折扣活动，如果酒水不参与打折，则将对应金额填写至此字段
        // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
        String undiscountableAmount = "0";
        // 卖家支付宝账号ID，用于支持一个签约账号下支持打款到不同的收款账号，(打款到sellerId对应的支付宝账号)
        // 如果该字段为空，则默认为与支付宝签约的商户的PID，也就是appid对应的PID
        String sellerId = "";
        // 订单描述，可以对交易或商品进行一个详细地描述，比如填写"购买商品2件共15.00元"
        String body = "购买商品3件共0.01元";
        // 商户操作员编号，添加此参数可以为商户操作员做销售统计
        String operatorId = "test_operator_id";
        // (必填) 商户门店编号，通过门店号和商家后台可以配置精准到门店的折扣信息，详询支付宝技术支持
        String storeId = "test_store_id";
        // 业务扩展参数，目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法)，详情请咨询支付宝技术支持
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId("2088100200300400500");
        // 支付超时，定义为120分钟
        String timeoutExpress = "120m";
        // 商品明细列表，需填写购买商品详细信息，
        List<GoodsDetail> goodsDetailList = new ArrayList<GoodsDetail>();
        // 创建一个商品信息，参数含义分别为商品id（使用国标）、名称、单价（单位为分）、数量，如果需要添加商品类别，详见GoodsDetail
        GoodsDetail goods1 = GoodsDetail.newInstance("goods_id001", "xxx小面包", 1000, 1);
        // 创建好一个商品后添加至商品明细列表
        goodsDetailList.add(goods1);
        // 继续创建并添加第一条商品信息，用户购买的产品为“黑人牙刷”，单价为5.00元，购买了两件
        GoodsDetail goods2 = GoodsDetail.newInstance("goods_id002", "xxx牙刷", 500, 2);
        goodsDetailList.add(goods2);
        // 创建扫码支付请求builder，设置请求参数
        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
                .setSubject(subject).setTotalAmount(totalAmount).setOutTradeNo(outTradeNo)
                .setUndiscountableAmount(undiscountableAmount).setSellerId(sellerId).setBody(body)
                .setOperatorId(operatorId).setStoreId(storeId).setExtendParams(extendParams)
                .setTimeoutExpress(timeoutExpress)
                //支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置(这个时候界面为静态界面,需要服务器回调)
                //回调方式 可以用dwr框架,也可以再客户端做一个轮训查询,服务器做缓存既可
                .setNotifyUrl(AlipayConfig.notify_url)
                .setGoodsDetailList(goodsDetailList);

        AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
        switch (result.getTradeStatus()) {
            case SUCCESS:
                logger.info("支付宝预下单成功: )");

                AlipayTradePrecreateResponse resultResponse = result.getResponse();
                dumpResponse(resultResponse);

                String realPath = request.getSession().getServletContext().getRealPath("/");
                File destFile = new File(realPath + "/upload/");
                if (!destFile.exists()) {
                    destFile.mkdirs();
                }
                // 需要修改为运行机器上的路径
                String fileName = String.format("qr-%s.png", resultResponse.getOutTradeNo());
                String filePath = String.format(destFile.getPath() + "/qr-%s.png", resultResponse.getOutTradeNo());

                logger.info("destFile.getPath():" + destFile.getPath());
                logger.info("filePath:" + filePath);
                request.setAttribute("fileName", "upload/" + fileName);
                request.setAttribute("outTradeNo", outTradeNo);
                //生成二维码文件
                ZxingUtils.getQRCodeImge(resultResponse.getQrCode(), 256, filePath);
                break;

            case FAILED:
                logger.error("支付宝预下单失败!!!");
                break;

            case UNKNOWN:
                logger.error("系统异常，预下单状态未知!!!");
                break;

            default:
                logger.error("不支持的交易状态，交易返回异常!!!");
                break;
        }
        return PATH + "precreate";
    }

    // 简单打印应答
    private void dumpResponse(AlipayResponse response) {
        if (response != null) {
            logger.info(String.format("code:%s, msg:%s", response.getCode(), response.getMsg()));
            if (StringUtils.isNotEmpty(response.getSubCode())) {
                logger.info(String.format("subCode:%s, subMsg:%s", response.getSubCode(), response.getSubMsg()));
            }
            logger.info("body:" + response.getBody());
        }
    }

    @RequestMapping("query.html")
    public String query() throws Exception {
        logger.info("query...");
        return PATH + "query";
    }

    @RequestMapping("doquery.html")
    public String doQuery() throws Exception {
        logger.info("doQuery...");
        logger.info("requestParams:{}", JSON.toJSONString(request.getParameterMap()));
        if (StringUtils.isNotEmpty(request.getParameter("outTradeNo"))) {
            AlipayTradeService tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

            // (必填) 商户订单号，通过此商户订单号查询当面付的交易状态
            String outTradeNo = request.getParameter("outTradeNo");
            AlipayTradeQueryRequestBuilder builder = new AlipayTradeQueryRequestBuilder().setOutTradeNo(outTradeNo);
            AlipayF2FQueryResult result = tradeService.queryTradeResult(builder);

            request.setAttribute("outTradeNo", outTradeNo);
            request.setAttribute("msg", result.getResponse().getBody());
            switch (result.getTradeStatus()) {
                case SUCCESS:
                    logger.info("查询返回该订单支付成功: )");

                    AlipayTradeQueryResponse resp = result.getResponse();

                    logger.info(resp.getTradeStatus());
                    logger.info(resp.getFundBillList());
                    break;

                case FAILED:
                    logger.error("查询返回该订单支付失败!!!");
                    break;

                case UNKNOWN:
                    logger.error("系统异常，订单支付状态未知!!!");
                    break;

                default:
                    logger.error("不支持的交易状态，交易返回异常!!!");
                    break;
            }
        }
        return PATH + "query";
    }

    @RequestMapping("refund.html")
    public String refund() throws Exception {
        logger.info("refund...");
        return PATH + "refund";
    }

    @RequestMapping("dorefund.html")
    public String doRefund() throws Exception {
        logger.info("doRefund...");
        logger.info("requestParams:{}", JSON.toJSONString(request.getParameterMap()));
        if (StringUtils.isNotEmpty(request.getParameter("outTradeNo"))) {
            // (必填) 外部订单号，需要退款交易的商户外部订单号
            String outTradeNo = request.getParameter("outTradeNo");
            // (必填) 退款金额，该金额必须小于等于订单的支付金额，单位为元
            String refundAmount = request.getParameter("refundAmount");
            // (可选，需要支持重复退货时必填) 商户退款请求号，相同支付宝交易号下的不同退款请求号对应同一笔交易的不同退款申请，
            // 对于相同支付宝交易号下多笔相同商户退款请求号的退款交易，支付宝只会进行一次退款
            String outRequestNo = request.getParameter("outRequestNo");
            // (必填) 退款原因，可以说明用户退款原因，方便为商家后台提供统计
            String refundReason = request.getParameter("refundReason");
            // (必填) 商户门店编号，退款情况下可以为商家后台提供退款权限判定和统计等作用，详询支付宝技术支持
            String storeId = request.getParameter("storeId");

            AlipayTradeRefundRequestBuilder builder = new AlipayTradeRefundRequestBuilder()
                    .setOutTradeNo(outTradeNo)
                    .setRefundAmount(refundAmount)
                    .setRefundReason(refundReason)
                    .setOutRequestNo(outRequestNo)
                    .setStoreId(storeId);

            AlipayF2FRefundResult result = tradeService.tradeRefund(builder);
            request.setAttribute("msg", result.getResponse().getBody());
            request.setAttribute("outTradeNo", outTradeNo);
            request.setAttribute("refundAmount", refundAmount);
            request.setAttribute("outRequestNo", outRequestNo);
            request.setAttribute("refundReason", refundReason);
            switch (result.getTradeStatus()) {
                case SUCCESS:
                    logger.info("支付宝退款成功: )");
                    break;

                case FAILED:
                    logger.error("支付宝退款失败!!!");
                    break;

                case UNKNOWN:
                    logger.error("系统异常，订单退款状态未知!!!");
                    break;

                default:
                    logger.error("不支持的交易状态，交易返回异常!!!");
                    break;
            }
        }
        return PATH + "refund";
    }
}
